#include "modbusmsg.h"

Modbusmsg::Modbusmsg()
{

    readsetings = new QSettings("dataini/Data.ini", QSettings::IniFormat);
    m_thread = new QThread(this);
    this->moveToThread(m_thread);
    readsetings->moveToThread(m_thread);
    m_thread->start();

}

Modbusmsg::~Modbusmsg()
{
    if (m_thread->isRunning())
    {
        m_thread->quit();
        m_thread->wait();
        while (true == m_thread->isRunning());
    }
    readsetings->deleteLater();
    m_thread->deleteLater();
}

/*
  *@brief 请求帧封装块开始
  *@param
  *@author rqk
  *@data 2022-1-2
*/
//0f 10 功能码请求帧封装开始函数
void Modbusmsg::packageSartc(quint8 addr,quint8 funcode,quint16 startaddr,quint16 num,QByteArray ba,QVector<quint16>regs)
{
    switch(funcode)
    {
    case  15:funCode0f(addr,startaddr,num,ba);
        break;
    case  16:funCode10(addr,startaddr,regs);
        break;
    }
}
//01 03 功能码请求帧封装开始函数
void Modbusmsg::packageSartr(quint8 addr, quint8 funcode, quint16 startaddr, quint16 numreg)
{
    switch(funcode)
    {
    case  1: funCode01(addr,startaddr,numreg);
        break;
    case  3: funCode03(addr,startaddr,numreg);
        break;
    }
}
//01功能码报文封装函数
void Modbusmsg::funCode01(quint8 addr,quint16 startaddr,quint16 numreg)
{
    //mb_identifier+=1;
    msgbuf[0] = mb_identifier>>8; // 事务标识符
    msgbuf[1] = mb_identifier&0xFF;
    msgbuf[2] = MB_PROTOCOL>>8; //高位
    msgbuf[3] = MB_PROTOCOL&0xFF;//低位
    msgbuf[4] = (mb_lenght+6)*256;
    msgbuf[5] = (mb_lenght+6)&0xFF;
    msgbuf[6] = addr;
    msgbuf[7] = 0x01;
    msgbuf[8] = startaddr>>8;
    msgbuf[9] = startaddr&0xFF;
    msgbuf[10] = numreg>>8;
    msgbuf[11] = numreg&0xFF;
    QByteArray ba((char*)msgbuf,12);
    requestMessage = ba;
    emit package_over(ba);
    memset(msgbuf,0,12);
}
//03功能码报文封装函数
void Modbusmsg::funCode03(quint8 addr,quint16 startaddr,quint16 numreg)
{
    //mb_identifier+=1;
    msgbuf[0] = mb_identifier>>8;// 事务标识符
    msgbuf[1] = mb_identifier&0xFF;
    msgbuf[2] = MB_PROTOCOL>>8;//高位
    msgbuf[3] = MB_PROTOCOL&0xFF;//低位
    msgbuf[4] = (mb_lenght+6)>>8;
    msgbuf[5] = (mb_lenght+6)&0xFF;
    msgbuf[6] = addr;
    msgbuf[7] = 0x03;
    msgbuf[8] = startaddr>>8;
    msgbuf[9] = startaddr&0xFF;
    msgbuf[10] = numreg>>8;
    msgbuf[11] = numreg&0xFF;
    QByteArray ba((char*)msgbuf,12);
    requestMessage = ba;
    emit package_over(ba);
    memset(msgbuf,0,12);
}
//0f功能码报文封装函数
void Modbusmsg::funCode0f(quint8 addr,quint16 startaddr,quint16 num,QByteArray bas)
{
    //线圈字节计数
    quint8 sizenum=bas.size();
    //mb_identifier+=1;
    msgbuf[0] = mb_identifier>>8; // 事务标识符
    msgbuf[1] = mb_identifier&0xFF;
    msgbuf[2] = MB_PROTOCOL>>8; //高位
    msgbuf[3] = MB_PROTOCOL&0xFF;//低位
    msgbuf[4] =(7+sizenum)>>8;
    msgbuf[5] =(7+sizenum)&0xFF;
    msgbuf[6] = addr;
    msgbuf[7] = 0x0f;
    msgbuf[8] = startaddr>>8;
    msgbuf[9] = startaddr&0xFF;
    msgbuf[10] = num>>8;   //线圈数//高位
    msgbuf[11] = num&0xFF; //低位
    msgbuf[12] =(quint8)(sizenum) ;//字节计数
    for(quint16 i=0;i<sizenum;i++)
    {
        msgbuf[13+i] = (quint8)bas.at(i);
    }
    QByteArray ba((char*)msgbuf,(13+sizenum));
    requestMessage = ba;
    emit package_over(ba);
    memset(msgbuf,0,(13+sizenum));
    bas.clear();
}
//10功能码报文封装函数
void Modbusmsg::funCode10(quint8 addr,quint16 startaddr,QVector<quint16> addrvalue)
{
    //计算数据字节大小
    quint16 num= addrvalue.size();
    //mb_identifier+=1;
    msgbuf[0] = mb_identifier>>8; // 事务标识符
    msgbuf[1] = mb_identifier&0xFF;
    msgbuf[2] = MB_PROTOCOL>>8; //高位
    msgbuf[3] = MB_PROTOCOL&0xFF;//低位
    msgbuf[4] =(7+num*2)>>8;
    msgbuf[5] =(7+num*2)&0xFF;
    msgbuf[6] = addr;
    msgbuf[7] = 0x10;
    msgbuf[8] = startaddr>>8;
    msgbuf[9] = startaddr&0xFF;
    msgbuf[10] = num>>8;
    msgbuf[11] = num&0xFF; //低位
    msgbuf[12] =(quint8)(num*2) ;//字节计数
    for(quint8 k=13,z=0;z<num;z++)
    {
        msgbuf[k++] = (quint16)addrvalue.at(z)>>8;
        msgbuf[k++] = (quint16)addrvalue.at(z)&0xFF;
    }
    QByteArray ba((char*)msgbuf,(13+num*2));
    requestMessage = ba;
    emit package_over(ba);
    memset(msgbuf,0,(13+num*2));
    addrvalue.clear();
}
/*
  *@brief 请求帧封装块结束
  *@param
  *@author rqk
  *@data 2022-1-2
*/
/**************************************************解析响应报文函数*************************************************/

// 解析响应报文
bool Modbusmsg::TCPAnalysisMessage(QByteArray MessageArray)
{
    qDebug()<<"回应报文解析线程:"<<QThread::currentThreadId();
    bool analysisResult;
    /***************************************处理报文*************************************/
    //初始化报文字符串
    QString readMessage;

    //将十六进制数据转化成字符串
    readMessage = HexByteArrayToHexString(MessageArray, MessageArray.size(), 1);

    /***************************************显示报文*************************************/
    //显示响应报文
    emit showMsgtoUi("响应报文:\n"+readMessage);

    /***************************************解析报文*************************************/
    //报文合法性判断
    analysisResult = MessageLegalJudge(MessageArray, requestMessage);
    if(analysisResult == false)
    {
        return false;
    }

    //处理匹配的正常响应码
    switch(MessageArray.at(7))
    {
    case 1:
    {
        //0X01功能码报文处理函数
        analysisResult = TCP0X01FuncCodeProcess(MessageArray, requestMessage);
        break;
    }

    case 3:
    {
        //0X03功能码报文处理函数
        analysisResult = TCP0X03FuncCodeProcess(MessageArray, requestMessage);
        break;
    }

    case 15:
    {
        //0X0f功能码报文处理函数
        analysisResult = TCP0X0fFuncCodeProcess(MessageArray, requestMessage);
        break;
    }

    case 16:
    {
        //0X10功能码报文处理函数
        analysisResult = TCP0X10FuncCodeProcess(MessageArray, requestMessage);
        break;
    }
    }

    if(analysisResult = true)
    {
        //清空请求报文数组
        requestMessage.clear();
    }

    return analysisResult;
}

// 报文合法性判断函数
bool Modbusmsg::MessageLegalJudge(QByteArray MessageArr, QByteArray requestMessageArr)
{
    bool Result;
    //1. 判断接收到的报文长度是否合法，合法最小长度为写入请求报文的异常响应报文，为9字节
    if(MessageArr.size() < 9)
    {
        //消息窗口显示信息
        emit showlogmsg("报文长度有误！");
        return false;
    }

    //2. 判断接收到的报文与请求报文的事务元标识符是否一致
    if((MessageArr.at(0) != requestMessageArr.at(0)) || (MessageArr.at(1) != requestMessageArr.at(1)))
    {
        //消息窗口显示信息
        emit showlogmsg("收到的报文不是请求报文的响应报文！");
        return false;
    }

    //3. 判断接收到的报文的协议标识是否是Modbus协议
    if((MessageArr.at(2) != 0) || (MessageArr.at(3) != 0))
    {
        //消息窗口显示信息
        emit showlogmsg("收到的报文不是Modbus报文！");
        return false;
    }

    //4. 判断接收到的报文的长度的数据与其后的字节长度是否匹配
    if((MessageArr.size() - 6) != BondTwoUint8ToUint16(MessageArr.at(4),MessageArr.at(5)))
    {
        //消息窗口显示信息
        emit showlogmsg("收到的报文的长度字段出现错误！");
        return false;
    }

    //5. 判断接收到的报文的单元标识符是否与请求报文一致
    if(MessageArr.at(6) != requestMessageArr.at(6))
    {
        //消息窗口显示信息
        emit showlogmsg("该报文的发送从站不是请求的从站！");
        return false;
    }

    //6. 判断接收到的报文的功能码是否合法
    Result = FuncCodeLegalJudge(MessageArr);
    if(Result == false)
    {
        return false;
    }

    //7. 处理匹配的异常响应码信息
    Result = TCPExceptionCodeProcess(MessageArr);
    if(Result == false)
    {
        return false;
    }

    //8. 判断请求与响应的功能码是否一致
    if(MessageArr.at(7) != requestMessageArr.at(7))
    {
        //消息窗口显示信息
        emit showlogmsg("收到报文的功能码与请求报文不一致！");
        return false;
    }

    return true;
}

// 功能码合法性判断函数
bool Modbusmsg::FuncCodeLegalJudge(QByteArray MessageArr)
{
    if(MessageArr.at(7) != 1
            &&MessageArr.at(7) != 3
            &&MessageArr.at(7) != 15
            &&MessageArr.at(7) != 16
            &&(quint8)MessageArr.at(7) != 0x81
            &&(quint8)MessageArr.at(7) != 0x83
            &&(quint8)MessageArr.at(7) != 0x8f
            &&(quint8)MessageArr.at(7) != 0x90)
    {
        //消息窗口显示信息
        emit showlogmsg("该报文的功能码无法识别！");
        return false;
    }
    else
    {
        return true;
    }
}

//异常码报文处理函数
bool Modbusmsg::TCPExceptionCodeProcess(QByteArray MessageArr)
{
    QString exceptionPrompt;
    //处理匹配的异常响应码信息
    switch ((quint8)MessageArr.at(7))
    {
    case 0x81:
    {
        exceptionPrompt = "读多个线圈的请求报文出现异常！";
        break;
    }
    case 0x83:
    {
        exceptionPrompt = "读多个寄存器的请求报文出现异常！";
        break;
    }
    case 0x8f:
    {
        exceptionPrompt = "写入多个线圈的请求报文出现异常！";
        break;
    }
    case 0x90:
    {
        exceptionPrompt = "写入多个寄存器的请求报文出现异常！";
        break;
    }
    default:
    {
        return true;
    }
    }

    //获取异常码
    quint8 exceptionCode = (quint8)MessageArr.at(8);

    emit showlogmsg(exceptionPrompt);

    //异常码判断
    TCPExceptionCodeJudge(exceptionCode);

    return false;
}

// 异常码判断函数
void Modbusmsg::TCPExceptionCodeJudge(quint8 excCode)
{
    QString exceptionCodePrompt;
    switch (excCode)
    {
    case 0x01:
    {
        exceptionCodePrompt = "异常码为：01 非法功能";
        break;
    }
    case 0x02:
    {
        exceptionCodePrompt = "异常码为：02 非法数据地址";
        break;
    }
    case 0x03:
    {
        exceptionCodePrompt = "异常码为：03 非法数据值";
        break;
    }
    }
    //消息窗口显示信息
    emit showlogmsg(exceptionCodePrompt);
    return;
}

//0X01功能码报文处理函数
bool Modbusmsg::TCP0X01FuncCodeProcess(QByteArray MessageArr, QByteArray requestMessageArr)
{
    //处理匹配的正常响应码
    QString dataObtained;
    quint8 numOfByte;
    quint16 length;

    //判断响应报文的长度是否符合最低要求
    if(MessageArr.size() < 10)
    {
        //消息窗口显示信息
        emit showlogmsg("响应报文的长度异常！");
        return false;
    }

    //先判断响应报文的字节数是否和请求报文需要的对应
    //求出请求报文中想读的线圈数量number
    quint16 number = BondTwoUint8ToUint16((quint8)requestMessageArr.at(10),(quint8)requestMessageArr.at(11));

    //判断响应报文的字节数是否和满足number需要的字节数匹配
    //求出字节数
    numOfByte = (number + 7) / 8;

    //如果响应报文的字节数和满足number需要的字节数不匹配，则显示错误信息
    if(numOfByte != (quint8)MessageArr.at(8))
    {
        //消息窗口显示信息
        emit showlogmsg("响应报文的字节数异常！");
        return false;
    }

    //判断自身长度字段是否异常
    length =(quint16)BondTwoUint8ToUint16(MessageArr.at(4),MessageArr.at(5));
    //如果不匹配，则输出错误信息
    if(length != MessageArr.size() - 6)
    {
        //消息窗口显示信息
        emit showlogmsg("响应报文自身的长度字段异常！");
        return false;
    }

    //判断自身字节数字段是否异常
    if((quint8)MessageArr.at(8) != (MessageArr.size() - 9))
    {
        //消息窗口显示信息
        emit showlogmsg("响应报文自身的字节数字段异常！");
        return false;
    }

    //取出所读的多个线圈，并显示，数据从第九位开始
    for(int i = 0; i < (quint8)MessageArr.at(8); i++)
    {
        //先转化为2进制字符串
        QString str = QString::number((quint8)MessageArr.at(9 + i),2);
        //再转化为2进制整形，由二进制整形转化为8位2进制字符串前面自动补0，从而保证8位
        str = QString("%1").arg((quint8)str.toInt(NULL,2),8,2,QChar('0'));
        //8bit字节倒转
        byteReverse(str);
        //添加到数据中
        dataObtained += str;
        coildata +=str;
    }
    quint16 startaddr = BondTwoUint8ToUint16(requestMessageArr.at(8),requestMessageArr.at(9));
    quint16 num = BondTwoUint8ToUint16(requestMessageArr.at(10),requestMessageArr.at(11));
    emit wirtTablec(num,startaddr,dataObtained);
    //去除填充的0位，读出请求报文请求的线圈数
    dataObtained = dataObtained.left(number);
    //提示响应报文解析成功
    //消息窗口显示信息
    emit showlogmsg("多线圈读取成功!");
    emit showlogmsg(dataObtained);
    WirteCoilDataToIni(startaddr,coildata);
    return true;
}

//0X03功能码报文处理函数
bool Modbusmsg::TCP0X03FuncCodeProcess(QByteArray MessageArr, QByteArray requestMessageArr)
{
    //处理匹配的正常响应码
    QString dataObtained;
    quint16 length;

    //判断响应报文的长度是否符合最低要求
    if(MessageArr.size() < 10)
    {
        //消息窗口显示信息
        emit showlogmsg("响应报文的长度异常！");
        return false;
    }

    //先判断响应报文的字节数是否和请求报文需要的对应
    //求出请求报文中想读的寄存器数量number
    quint16 number = BondTwoUint8ToUint16((quint8)requestMessageArr.at(10),(quint8)requestMessageArr.at(11));

    //如果响应报文的字节数和满足number需要的字节数不匹配，则显示错误信息
    if(2*number != (quint8)MessageArr.at(8))
    {
        //消息窗口显示信息
        emit showlogmsg("响应报文的字节数异常！");
        return false;
    }

    //判断自身长度字段是否异常
    length =(quint16)BondTwoUint8ToUint16(MessageArr.at(4),MessageArr.at(5));
    //如果不匹配，则输出错误信息
    if(length != MessageArr.size() - 6)
    {
        //消息窗口显示信息
        emit showlogmsg("响应报文自身的长度字段异常！");
        return false;
    }

    //判断自身字节数字段是否异常
    if((quint8)MessageArr.at(8) != (MessageArr.size() - 9))
    {
        //消息窗口显示信息
        emit showlogmsg("响应报文自身的字节数字段异常！");
        return false;
    }
    quint16 startaddr = BondTwoUint8ToUint16(requestMessageArr.at(8),requestMessageArr.at(9));
    quint16 num = BondTwoUint8ToUint16(requestMessageArr.at(10),requestMessageArr.at(11));
    //取出所读的多个寄存器，并显示，数据从第9个字节开始
    for(int i = 0;i < (quint8)MessageArr.at(8); i += 2)
    {
        dataObtained += QString::number(BondTwoUint8ToUint16((quint8)MessageArr.at(9 + i),(quint8)MessageArr.at(10 + i)));
        bar.push_back((quint16)dataObtained.toInt());
        dataObtained += " ";
    }
    emit wirtTabler(num,startaddr,bar);
    //消息窗口显示信息
    emit showlogmsg("多寄存器读取成功!");
    emit showlogmsg(dataObtained);
    WirteRegsDataToIni(startaddr,num,bar);
    return true;
}

//0X0f功能码报文处理函数
bool Modbusmsg::TCP0X0fFuncCodeProcess(QByteArray MessageArr, QByteArray requestMessageArr)
{
    //处理匹配的正常响应码
    quint16 messageArrayBeginAddress;
    quint16 requestMessageBegainAddress;
    quint16 length;
    quint16 messageArrayByteNum;
    quint16 requestMessageByteNum;

    //判断响应报文的长度是否符合最低要求，写入请求报文的响应报文为固定长度12
    if(MessageArr.size() != 12)
    {
        //消息窗口显示信息
        emit showlogmsg("响应报文的长度异常！");
        return false;
    }

    //判断自身长度字段是否异常
    length =(quint16)BondTwoUint8ToUint16(MessageArr.at(4),MessageArr.at(5));
    //如果不匹配，则输出错误信息
    if(length != 6)
    {
        //消息窗口显示信息
        emit showlogmsg("响应报文自身的长度字段异常！");
        return false;
    }

    //判断收到的报文的起始地址字段是否与请求报文匹配
    messageArrayBeginAddress = (quint16)BondTwoUint8ToUint16(MessageArr.at(8),MessageArr.at(9));
    requestMessageBegainAddress = (quint16)BondTwoUint8ToUint16(requestMessageArr.at(8),requestMessageArr.at(9));
    if(messageArrayBeginAddress != requestMessageBegainAddress)
    {
        //消息窗口显示信息
        emit showlogmsg("收到的报文的起始地址字段与请求报文不匹配！");
        return false;
    }

    //判断收到的报文的数量字段是否与请求报文匹配
    messageArrayByteNum = (quint16)BondTwoUint8ToUint16(MessageArr.at(10),MessageArr.at(11));
    requestMessageByteNum = (quint16)BondTwoUint8ToUint16(requestMessageArr.at(10),requestMessageArr.at(11));
    if(messageArrayByteNum != requestMessageByteNum)
    {
        //消息窗口显示信息
        emit showlogmsg("收到的报文的数量字段与请求报文不匹配！");
        return false;
    }

    //提示响应报文解析成功
    //消息窗口显示信息
    emit showlogmsg("多线圈写入成功!");
    return true;
}

//0X10功能码报文处理函数
bool Modbusmsg::TCP0X10FuncCodeProcess(QByteArray MessageArr, QByteArray requestMessageArr)
{
    //处理匹配的正常响应码
    quint16 messageArrayBeginAddress;
    quint16 requestMessageBegainAddress;
    quint16 length;
    quint16 messageArrayByteNum;
    quint16 requestMessageByteNum;

    //判断响应报文的长度是否符合最低要求，写入请求报文的响应报文为固定长度12
    if(MessageArr.size() != 12)
    {

        //消息窗口显示信息
        emit showlogmsg("响应报文的长度异常！");
        return false;
    }

    //判断自身长度字段是否异常
    length =(quint16)BondTwoUint8ToUint16(MessageArr.at(4),MessageArr.at(5));
    //如果不匹配，则输出错误信息
    if(length != 6)
    {
        //消息窗口显示信息
        emit showlogmsg("响应报文自身的长度字段异常！");
        return false;
    }

    //判断收到的报文的起始地址字段是否与请求报文匹配
    messageArrayBeginAddress = (quint16)BondTwoUint8ToUint16(MessageArr.at(8),MessageArr.at(9));
    requestMessageBegainAddress = (quint16)BondTwoUint8ToUint16(requestMessageArr.at(8),requestMessageArr.at(9));
    if(messageArrayBeginAddress != requestMessageBegainAddress)
    {
        //消息窗口显示信息
        emit showlogmsg("收到的报文的起始地址字段与请求报文不匹配！");
        return false;
    }

    //判断收到的报文的数量字段是否与请求报文匹配
    messageArrayByteNum = (quint16)BondTwoUint8ToUint16(MessageArr.at(10),MessageArr.at(11));
    requestMessageByteNum = (quint16)BondTwoUint8ToUint16(requestMessageArr.at(10),requestMessageArr.at(11));
    if(messageArrayByteNum != requestMessageByteNum)
    {
        //消息窗口显示信息
        emit showlogmsg("收到的报文的数量字段与请求报文不匹配！");

        return false;
    }

    //提示响应报文解析成功
    //消息窗口显示信息
    emit showlogmsg("多寄存器写入成功!");
    return true;
}
//将读取的寄存器数据写入ini文件
void Modbusmsg::WirteRegsDataToIni(quint16 startaddr, quint16 num,QVector<quint16> ba)
{
    for(quint16 i=0,k=startaddr;i<num;i++,k++)
    {
      //写入文件
      QString s = "Section" + QString::number(k+1) + "/regi";
      readsetings->setValue(s,ba.at(i));
    }
}
//将读取的线圈数据写入ini文件
void Modbusmsg::WirteCoilDataToIni(quint16 satrt, QString CoilData)
{
    //更新ini文件数据
    for(int j=0,k=satrt;j<CoilData.length();j++,k++)
    {
        QString s = "Section" + QString::number(k+1) + "/coil";
        quint8 coildata;
        if(CoilData.at(j)=='1')
        {
            coildata = 1;
        }
        else
        {
            coildata = 0;
        }
        readsetings->setValue(s,coildata);
    }
    coildata.clear();
}

